#ifndef     __CAN_RECEIVER_H
#define     __CAN_RECEIVER_H

#include "./frame.h"
#include "./can_frame.h"
#include "../rtos/message_queue.h"
#include "../utils/byte_alloc.h"
#include "../utils/log.h"
#include <string.h>

namespace conn {

template <uint32_t MQ_CAPCCITY, uint32_t BUFF_SIZE>
struct CANReceiver : private NoCopyable {

public:
    void init() {
        m_mq.init();
    }

    inline void post(const CANFrame &frame) { m_mq.post(frame);}

    void recv_next(usize_t len) {
        m_alloc.next(len);
    }

    void recv_from(Frame &frame) {
        CANFrame can_frame;

        for (;;) {
        begin:
            m_mq.poll(can_frame);

            if ((can_frame.buf[0] != proto::HEAD0) ||
                (can_frame.buf[1] != proto::HEAD1) ||
                (can_frame.buf[2] < 8)) {
                println("recv heaf invalid");
                continue;
            }

            usize_t len = can_frame.buf[2];
            uint8_t *buf = m_alloc.alloc(len);

            memcpy(buf, can_frame.buf, can_frame.len);
            usize_t index = can_frame.len;

            for (; index < len;) {
                if (m_mq.poll(can_frame, 10) != osOK) {
                    println("recv timeout");
                    goto begin;
                }
                memcpy(buf + index, can_frame.buf, can_frame.len);
                index += can_frame.len;
            }

            uint8_t sum = alg::xor_sum(buf + 3, len - 5);
            if (buf[len - 2] != sum) {
                println("check sum fail");
                continue;
            }

            if (buf[len - 1] != proto::END) {
                println("recv end fail");
                continue;
            }
            frame.buf = buf;
            frame.len = len;
            return;
        }
    }


private:
    rtos::lazy::MessageQueue<CANFrame, MQ_CAPCCITY> m_mq;
    ByteAlloc<BUFF_SIZE> m_alloc;
};

}

#endif
